/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"imgFrame.h"#include"ImageRegion.h"#include"ShutdownTracker.h"#include"prenv.h"#include"gfx2DGlue.h"#include"gfxPlatform.h"#include"gfxPrefs.h"#include"gfxUtils.h"#include"gfxAlphaRecovery.h"#include"GeckoProfiler.h"#include"MainThreadUtils.h"#include"mozilla/CheckedInt.h"#include"mozilla/gfx/Tools.h"#include"mozilla/layers/SourceSurfaceSharedData.h"#include"mozilla/layers/SourceSurfaceVolatileData.h"#include"mozilla/Likely.h"#include"mozilla/MemoryReporting.h"#include"nsMargin.h"#include"nsThreadUtils.h"namespacemozilla{usingnamespacegfx;namespaceimage{staticvoidScopedMapRelease(void*aMap){deletestatic_cast<DataSourceSurface::ScopedMap*>(aMap);}staticint32_tVolatileSurfaceStride(constIntSize&size,SurfaceFormatformat){// Stride must be a multiple of four or cairo will complain.return(size.width*BytesPerPixel(format)+0x3)&~0x3;}staticalready_AddRefed<DataSourceSurface>CreateLockedSurface(DataSourceSurface*aSurface,constIntSize&size,SurfaceFormatformat){// Shared memory is never released until the surface itself is releasedif(aSurface->GetType()==SurfaceType::DATA_SHARED){RefPtr<DataSourceSurface>surf(aSurface);returnsurf.forget();}DataSourceSurface::ScopedMap*smap=newDataSourceSurface::ScopedMap(aSurface,DataSourceSurface::READ_WRITE);if(smap->IsMapped()){// The ScopedMap is held by this DataSourceSurface.RefPtr<DataSourceSurface>surf=Factory::CreateWrappingDataSourceSurface(smap->GetData(),aSurface->Stride(),size,format,&ScopedMapRelease,static_cast<void*>(smap));if(surf){returnsurf.forget();}}deletesmap;returnnullptr;}staticalready_AddRefed<DataSourceSurface>AllocateBufferForImage(constIntSize&size,SurfaceFormatformat,boolaIsAnimated=false){int32_tstride=VolatileSurfaceStride(size,format);if(!aIsAnimated&&gfxPrefs::ImageMemShared()){RefPtr<SourceSurfaceSharedData>newSurf=newSourceSurfaceSharedData();if(newSurf->Init(size,stride,format)){returnnewSurf.forget();}}else{RefPtr<SourceSurfaceVolatileData>newSurf=newSourceSurfaceVolatileData();if(newSurf->Init(size,stride,format)){returnnewSurf.forget();}}returnnullptr;}staticboolClearSurface(DataSourceSurface*aSurface,constIntSize&aSize,SurfaceFormataFormat){int32_tstride=aSurface->Stride();uint8_t*data=aSurface->GetData();MOZ_ASSERT(data);if(aFormat==SurfaceFormat::B8G8R8X8){// Skia doesn't support RGBX surfaces, so ensure the alpha value is set// to opaque white. While it would be nice to only do this for Skia,// imgFrame can run off main thread and past shutdown where// we might not have gfxPlatform, so just memset everytime instead.memset(data,0xFF,stride*aSize.height);}elseif(aSurface->OnHeap()){// We only need to memset it if the buffer was allocated on the heap.// Otherwise, it's allocated via mmap and refers to a zeroed page and will// be COW once it's written to.memset(data,0,stride*aSize.height);}returntrue;}voidMarkSurfaceShared(SourceSurface*aSurface){// Depending on what requested the image decoding, the buffer may or may not// end up being shared with another process (e.g. put in a painted layer,// used inside a canvas). If not shared, we should ensure are not keeping the// handle only because we have yet to share it.if(aSurface&&aSurface->GetType()==SurfaceType::DATA_SHARED){autosharedSurface=static_cast<SourceSurfaceSharedData*>(aSurface);sharedSurface->FinishedSharing();}}// Returns true if an image of aWidth x aHeight is allowed and legal.staticboolAllowedImageSize(int32_taWidth,int32_taHeight){// reject over-wide or over-tall imagesconstint32_tk64KLimit=0x0000FFFF;if(MOZ_UNLIKELY(aWidth>k64KLimit||aHeight>k64KLimit)){NS_WARNING("image too big");returnfalse;}// protect against invalid sizesif(MOZ_UNLIKELY(aHeight<=0||aWidth<=0)){returnfalse;}// check to make sure we don't overflow a 32-bitCheckedInt32requiredBytes=CheckedInt32(aWidth)*CheckedInt32(aHeight)*4;if(MOZ_UNLIKELY(!requiredBytes.isValid())){NS_WARNING("width or height too large");returnfalse;}returntrue;}staticboolAllowedImageAndFrameDimensions(constnsIntSize&aImageSize,constnsIntRect&aFrameRect){if(!AllowedImageSize(aImageSize.width,aImageSize.height)){returnfalse;}if(!AllowedImageSize(aFrameRect.width,aFrameRect.height)){returnfalse;}nsIntRectimageRect(0,0,aImageSize.width,aImageSize.height);if(!imageRect.Contains(aFrameRect)){NS_WARNING("Animated image frame does not fit inside bounds of image");}returntrue;}imgFrame::imgFrame():mMonitor("imgFrame"),mDecoded(0,0,0,0),mLockCount(0),mTimeout(FrameTimeout::FromRawMilliseconds(100)),mDisposalMethod(DisposalMethod::NOT_SPECIFIED),mBlendMethod(BlendMethod::OVER),mAborted(false),mFinished(false),mOptimizable(false),mPalettedImageData(nullptr),mPaletteDepth(0),mNonPremult(false),mCompositingFailed(false){}imgFrame::~imgFrame(){#ifdef DEBUGMonitorAutoLocklock(mMonitor);MOZ_ASSERT(mAborted||AreAllPixelsWritten());MOZ_ASSERT(mAborted||mFinished);#endiffree(mPalettedImageData);mPalettedImageData=nullptr;}nsresultimgFrame::InitForDecoder(constnsIntSize&aImageSize,constnsIntRect&aRect,SurfaceFormataFormat,uint8_taPaletteDepth/* = 0 */,boolaNonPremult/* = false */,boolaIsAnimated/* = false */){// Assert for properties that should be verified by decoders,// warn for properties related to bad content.if(!AllowedImageAndFrameDimensions(aImageSize,aRect)){NS_WARNING("Should have legal image size");mAborted=true;returnNS_ERROR_FAILURE;}mImageSize=aImageSize;mFrameRect=aRect;// We only allow a non-trivial frame rect (i.e., a frame rect that doesn't// cover the entire image) for paletted animation frames. We never draw those// frames directly; we just use FrameAnimator to composite them and produce a// BGRA surface that we actually draw. We enforce this here to make sure that// imgFrame::Draw(), which is responsible for drawing all other kinds of// frames, never has to deal with a non-trivial frame rect.if(aPaletteDepth==0&&!mFrameRect.IsEqualEdges(IntRect(IntPoint(),mImageSize))){MOZ_ASSERT_UNREACHABLE("Creating a non-paletted imgFrame with a ""non-trivial frame rect");returnNS_ERROR_FAILURE;}mFormat=aFormat;mPaletteDepth=aPaletteDepth;mNonPremult=aNonPremult;if(aPaletteDepth!=0){// We're creating for a paletted image.if(aPaletteDepth>8){NS_WARNING("Should have legal palette depth");NS_ERROR("This Depth is not supported");mAborted=true;returnNS_ERROR_FAILURE;}// Use the fallible allocator here. Paletted images always use 1 byte per// pixel, so calculating the amount of memory we need is straightforward.size_tdataSize=PaletteDataLength()+mFrameRect.Area();mPalettedImageData=static_cast<uint8_t*>(calloc(dataSize,sizeof(uint8_t)));if(!mPalettedImageData){NS_WARNING("Call to calloc for paletted image data should succeed");}NS_ENSURE_TRUE(mPalettedImageData,NS_ERROR_OUT_OF_MEMORY);}else{MOZ_ASSERT(!mLockedSurface,"Called imgFrame::InitForDecoder() twice?");mRawSurface=AllocateBufferForImage(mFrameRect.Size(),mFormat,aIsAnimated);if(!mRawSurface){mAborted=true;returnNS_ERROR_OUT_OF_MEMORY;}mLockedSurface=CreateLockedSurface(mRawSurface,mFrameRect.Size(),mFormat);if(!mLockedSurface){NS_WARNING("Failed to create LockedSurface");mAborted=true;returnNS_ERROR_OUT_OF_MEMORY;}if(!ClearSurface(mRawSurface,mFrameRect.Size(),mFormat)){NS_WARNING("Could not clear allocated buffer");mAborted=true;returnNS_ERROR_OUT_OF_MEMORY;}}returnNS_OK;}nsresultimgFrame::InitWithDrawable(gfxDrawable*aDrawable,constnsIntSize&aSize,constSurfaceFormataFormat,SamplingFilteraSamplingFilter,uint32_taImageFlags,gfx::BackendTypeaBackend){// Assert for properties that should be verified by decoders,// warn for properties related to bad content.if(!AllowedImageSize(aSize.width,aSize.height)){NS_WARNING("Should have legal image size");mAborted=true;returnNS_ERROR_FAILURE;}mImageSize=aSize;mFrameRect=IntRect(IntPoint(0,0),aSize);mFormat=aFormat;mPaletteDepth=0;RefPtr<DrawTarget>target;boolcanUseDataSurface=gfxPlatform::GetPlatform()->CanRenderContentToDataSurface();if(canUseDataSurface){// It's safe to use data surfaces for content on this platform, so we can// get away with using volatile buffers.MOZ_ASSERT(!mLockedSurface,"Called imgFrame::InitWithDrawable() twice?");mRawSurface=AllocateBufferForImage(mFrameRect.Size(),mFormat);if(!mRawSurface){mAborted=true;returnNS_ERROR_OUT_OF_MEMORY;}mLockedSurface=CreateLockedSurface(mRawSurface,mFrameRect.Size(),mFormat);if(!mLockedSurface){NS_WARNING("Failed to create LockedSurface");mAborted=true;returnNS_ERROR_OUT_OF_MEMORY;}if(!ClearSurface(mRawSurface,mFrameRect.Size(),mFormat)){NS_WARNING("Could not clear allocated buffer");mAborted=true;returnNS_ERROR_OUT_OF_MEMORY;}target=gfxPlatform::CreateDrawTargetForData(mLockedSurface->GetData(),mFrameRect.Size(),mLockedSurface->Stride(),mFormat);}else{// We can't use data surfaces for content, so we'll create an offscreen// surface instead. This means if someone later calls RawAccessRef(), we// may have to do an expensive readback, but we warned callers about that in// the documentation for this method.MOZ_ASSERT(!mOptSurface,"Called imgFrame::InitWithDrawable() twice?");if(gfxPlatform::GetPlatform()->SupportsAzureContentForType(aBackend)){target=gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend,mFrameRect.Size(),mFormat);}else{target=gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mFrameRect.Size(),mFormat);}}if(!target||!target->IsValid()){mAborted=true;returnNS_ERROR_OUT_OF_MEMORY;}// Draw using the drawable the caller provided.RefPtr<gfxContext>ctx=gfxContext::CreateOrNull(target);MOZ_ASSERT(ctx);// Already checked the draw target above.gfxUtils::DrawPixelSnapped(ctx,aDrawable,SizeDouble(mFrameRect.Size()),ImageRegion::Create(ThebesRect(mFrameRect)),mFormat,aSamplingFilter,aImageFlags);if(canUseDataSurface&&!mLockedSurface){NS_WARNING("Failed to create VolatileDataSourceSurface");mAborted=true;returnNS_ERROR_OUT_OF_MEMORY;}if(!canUseDataSurface){// We used an offscreen surface, which is an "optimized" surface from// imgFrame's perspective.mOptSurface=target->Snapshot();}else{FinalizeSurface();}// If we reach this point, we should regard ourselves as complete.mDecoded=GetRect();mFinished=true;#ifdef DEBUGMonitorAutoLocklock(mMonitor);MOZ_ASSERT(AreAllPixelsWritten());#endifreturnNS_OK;}nsresultimgFrame::Optimize(DrawTarget*aTarget){MOZ_ASSERT(NS_IsMainThread());mMonitor.AssertCurrentThreadOwns();if(mLockCount>0||!mOptimizable){// Don't optimize right now.returnNS_OK;}// Check whether image optimization is disabled -- not thread safe!staticboolgDisableOptimize=false;staticboolhasCheckedOptimize=false;if(!hasCheckedOptimize){if(PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")){gDisableOptimize=true;}hasCheckedOptimize=true;}// Don't optimize during shutdown because gfxPlatform may not be available.if(ShutdownTracker::ShutdownHasStarted()){returnNS_OK;}if(gDisableOptimize){returnNS_OK;}if(mPalettedImageData||mOptSurface){returnNS_OK;}// XXX(seth): It's currently unclear if there's any reason why we can't// optimize non-premult surfaces. We should look into removing this.if(mNonPremult){returnNS_OK;}mOptSurface=gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mLockedSurface);if(mOptSurface==mLockedSurface){mOptSurface=nullptr;}if(mOptSurface){// There's no reason to keep our original surface around if we have an// optimized surface. Release our reference to it. This will leave// |mLockedSurface| as the only thing keeping it alive, so it'll get freed// below.mRawSurface=nullptr;}// Release all strong references to the surface's memory. If the underlying// surface is volatile, this will allow the operating system to free the// memory if it needs to.mLockedSurface=nullptr;mOptimizable=false;returnNS_OK;}DrawableFrameRefimgFrame::DrawableRef(){returnDrawableFrameRef(this);}RawAccessFrameRefimgFrame::RawAccessRef(){returnRawAccessFrameRef(this);}voidimgFrame::SetRawAccessOnly(){AssertImageDataLocked();// Lock our data and throw away the key.LockImageData();}imgFrame::SurfaceWithFormatimgFrame::SurfaceForDrawing(boolaDoPartialDecode,boolaDoTile,ImageRegion&aRegion,SourceSurface*aSurface){MOZ_ASSERT(NS_IsMainThread());mMonitor.AssertCurrentThreadOwns();if(!aDoPartialDecode){returnSurfaceWithFormat(newgfxSurfaceDrawable(aSurface,mImageSize),mFormat);}gfxRectavailable=gfxRect(mDecoded.x,mDecoded.y,mDecoded.width,mDecoded.height);if(aDoTile){// Create a temporary surface.// Give this surface an alpha channel because there are// transparent pixels in the padding or undecoded areaRefPtr<DrawTarget>target=gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mImageSize,SurfaceFormat::B8G8R8A8);if(!target){returnSurfaceWithFormat();}SurfacePatternpattern(aSurface,aRegion.GetExtendMode(),Matrix::Translation(mDecoded.x,mDecoded.y));target->FillRect(ToRect(aRegion.Intersect(available).Rect()),pattern);RefPtr<SourceSurface>newsurf=target->Snapshot();returnSurfaceWithFormat(newgfxSurfaceDrawable(newsurf,mImageSize),target->GetFormat());}// Not tiling, and we have a surface, so we can account for// a partial decode just by twiddling parameters.aRegion=aRegion.Intersect(available);IntSizeavailableSize(mDecoded.width,mDecoded.height);returnSurfaceWithFormat(newgfxSurfaceDrawable(aSurface,availableSize),mFormat);}boolimgFrame::Draw(gfxContext*aContext,constImageRegion&aRegion,SamplingFilteraSamplingFilter,uint32_taImageFlags,floataOpacity){AUTO_PROFILER_LABEL("imgFrame::Draw",GRAPHICS);MOZ_ASSERT(NS_IsMainThread());NS_ASSERTION(!aRegion.Rect().IsEmpty(),"Drawing empty region!");NS_ASSERTION(!aRegion.IsRestricted()||!aRegion.Rect().Intersect(aRegion.Restriction()).IsEmpty(),"We must be allowed to sample *some* source pixels!");MOZ_ASSERT(mFrameRect.IsEqualEdges(IntRect(IntPoint(),mImageSize)),"Directly drawing an image with a non-trivial frame rect!");if(mPalettedImageData){MOZ_ASSERT_UNREACHABLE("Directly drawing a paletted image!");returnfalse;}MonitorAutoLocklock(mMonitor);// Possibly convert this image into a GPU texture, this may also cause our// mLockedSurface to be released and the OS to release the underlying memory.Optimize(aContext->GetDrawTarget());booldoPartialDecode=!AreAllPixelsWritten();RefPtr<SourceSurface>surf=GetSourceSurfaceInternal();if(!surf){returnfalse;}gfxRectimageRect(0,0,mImageSize.width,mImageSize.height);booldoTile=!imageRect.Contains(aRegion.Rect())&&!(aImageFlags&imgIContainer::FLAG_CLAMP);ImageRegionregion(aRegion);SurfaceWithFormatsurfaceResult=SurfaceForDrawing(doPartialDecode,doTile,region,surf);if(surfaceResult.IsValid()){gfxUtils::DrawPixelSnapped(aContext,surfaceResult.mDrawable,imageRect.Size(),region,surfaceResult.mFormat,aSamplingFilter,aImageFlags,aOpacity);}// Image got put into a painted layer, it will not be shared with another// process.MarkSurfaceShared(surf);returntrue;}nsresultimgFrame::ImageUpdated(constnsIntRect&aUpdateRect){MonitorAutoLocklock(mMonitor);returnImageUpdatedInternal(aUpdateRect);}nsresultimgFrame::ImageUpdatedInternal(constnsIntRect&aUpdateRect){mMonitor.AssertCurrentThreadOwns();mDecoded.UnionRect(mDecoded,aUpdateRect);// Clamp to the frame rect to ensure that decoder bugs don't result in a// decoded rect that extends outside the bounds of the frame rect.mDecoded.IntersectRect(mDecoded,mFrameRect);returnNS_OK;}voidimgFrame::Finish(OpacityaFrameOpacity/* = Opacity::SOME_TRANSPARENCY */,DisposalMethodaDisposalMethod/* = DisposalMethod::KEEP */,FrameTimeoutaTimeout/* = FrameTimeout::FromRawMilliseconds(0) */,BlendMethodaBlendMethod/* = BlendMethod::OVER */,constMaybe<IntRect>&aBlendRect/* = Nothing() */,boolaFinalize/* = true */){MonitorAutoLocklock(mMonitor);MOZ_ASSERT(mLockCount>0,"Image data should be locked");mDisposalMethod=aDisposalMethod;mTimeout=aTimeout;mBlendMethod=aBlendMethod;mBlendRect=aBlendRect;ImageUpdatedInternal(GetRect());if(aFinalize){FinalizeSurfaceInternal();}mFinished=true;// The image is now complete, wake up anyone who's waiting.mMonitor.NotifyAll();}uint32_timgFrame::GetImageBytesPerRow()const{mMonitor.AssertCurrentThreadOwns();if(mRawSurface){returnmFrameRect.width*BytesPerPixel(mFormat);}if(mPaletteDepth){returnmFrameRect.width;}return0;}uint32_timgFrame::GetImageDataLength()const{returnGetImageBytesPerRow()*mFrameRect.height;}voidimgFrame::GetImageData(uint8_t**aData,uint32_t*aLength)const{MonitorAutoLocklock(mMonitor);GetImageDataInternal(aData,aLength);}voidimgFrame::GetImageDataInternal(uint8_t**aData,uint32_t*aLength)const{mMonitor.AssertCurrentThreadOwns();MOZ_ASSERT(mLockCount>0,"Image data should be locked");if(mLockedSurface){// TODO: This is okay for now because we only realloc shared surfaces on// the main thread after decoding has finished, but if animations want to// read frame data off the main thread, we will need to reconsider this.*aData=mLockedSurface->GetData();MOZ_ASSERT(*aData,"mLockedSurface is non-null, but GetData is null in GetImageData");}elseif(mPalettedImageData){*aData=mPalettedImageData+PaletteDataLength();MOZ_ASSERT(*aData,"mPalettedImageData is non-null, but result is null in GetImageData");}else{MOZ_ASSERT(false,"Have neither mLockedSurface nor mPalettedImageData in GetImageData");*aData=nullptr;}*aLength=GetImageDataLength();}uint8_t*imgFrame::GetImageData()const{uint8_t*data;uint32_tlength;GetImageData(&data,&length);returndata;}boolimgFrame::GetIsPaletted()const{returnmPalettedImageData!=nullptr;}voidimgFrame::GetPaletteData(uint32_t**aPalette,uint32_t*length)const{AssertImageDataLocked();if(!mPalettedImageData){*aPalette=nullptr;*length=0;}else{*aPalette=(uint32_t*)mPalettedImageData;*length=PaletteDataLength();}}uint32_t*imgFrame::GetPaletteData()const{uint32_t*data;uint32_tlength;GetPaletteData(&data,&length);returndata;}nsresultimgFrame::LockImageData(){MonitorAutoLocklock(mMonitor);MOZ_ASSERT(mLockCount>=0,"Unbalanced locks and unlocks");if(mLockCount<0){returnNS_ERROR_FAILURE;}mLockCount++;// If we are not the first lock, there's nothing to do.if(mLockCount!=1){returnNS_OK;}// If we're the first lock, but have the locked surface, we're OK.if(mLockedSurface){returnNS_OK;}// Paletted images don't have surfaces, so there's nothing to do.if(mPalettedImageData){returnNS_OK;}MOZ_ASSERT_UNREACHABLE("It's illegal to re-lock an optimized imgFrame");returnNS_ERROR_FAILURE;}voidimgFrame::AssertImageDataLocked()const{#ifdef DEBUGMonitorAutoLocklock(mMonitor);MOZ_ASSERT(mLockCount>0,"Image data should be locked");#endif}nsresultimgFrame::UnlockImageData(){MonitorAutoLocklock(mMonitor);MOZ_ASSERT(mLockCount>0,"Unlocking an unlocked image!");if(mLockCount<=0){returnNS_ERROR_FAILURE;}MOZ_ASSERT(mLockCount>1||mFinished||mAborted,"Should have Finish()'d or aborted before unlocking");mLockCount--;returnNS_OK;}voidimgFrame::SetOptimizable(){AssertImageDataLocked();MonitorAutoLocklock(mMonitor);mOptimizable=true;}voidimgFrame::FinalizeSurface(){MonitorAutoLocklock(mMonitor);FinalizeSurfaceInternal();}voidimgFrame::FinalizeSurfaceInternal(){mMonitor.AssertCurrentThreadOwns();// Not all images will have mRawSurface to finalize (i.e. paletted images).if(!mRawSurface||mRawSurface->GetType()!=SurfaceType::DATA_SHARED){return;}autosharedSurf=static_cast<SourceSurfaceSharedData*>(mRawSurface.get());sharedSurf->Finalize();}already_AddRefed<SourceSurface>imgFrame::GetSourceSurface(){MonitorAutoLocklock(mMonitor);returnGetSourceSurfaceInternal();}already_AddRefed<SourceSurface>imgFrame::GetSourceSurfaceInternal(){mMonitor.AssertCurrentThreadOwns();if(mOptSurface){if(mOptSurface->IsValid()){RefPtr<SourceSurface>surf(mOptSurface);returnsurf.forget();}else{mOptSurface=nullptr;}}if(mLockedSurface){RefPtr<SourceSurface>surf(mLockedSurface);returnsurf.forget();}if(!mRawSurface){returnnullptr;}returnCreateLockedSurface(mRawSurface,mFrameRect.Size(),mFormat);}AnimationDataimgFrame::GetAnimationData()const{MonitorAutoLocklock(mMonitor);MOZ_ASSERT(mLockCount>0,"Image data should be locked");uint8_t*data;if(mPalettedImageData){data=mPalettedImageData;}else{uint32_tlength;GetImageDataInternal(&data,&length);}boolhasAlpha=mFormat==SurfaceFormat::B8G8R8A8;returnAnimationData(data,PaletteDataLength(),mTimeout,GetRect(),mBlendMethod,mBlendRect,mDisposalMethod,hasAlpha);}voidimgFrame::Abort(){MonitorAutoLocklock(mMonitor);mAborted=true;// Wake up anyone who's waiting.mMonitor.NotifyAll();}boolimgFrame::IsAborted()const{MonitorAutoLocklock(mMonitor);returnmAborted;}boolimgFrame::IsFinished()const{MonitorAutoLocklock(mMonitor);returnmFinished;}voidimgFrame::WaitUntilFinished()const{MonitorAutoLocklock(mMonitor);while(true){// Return if we're aborted or complete.if(mAborted||mFinished){return;}// Not complete yet, so we'll have to wait.mMonitor.Wait();}}boolimgFrame::AreAllPixelsWritten()const{mMonitor.AssertCurrentThreadOwns();returnmDecoded.IsEqualInterior(mFrameRect);}boolimgFrame::GetCompositingFailed()const{MOZ_ASSERT(NS_IsMainThread());returnmCompositingFailed;}voidimgFrame::SetCompositingFailed(boolval){MOZ_ASSERT(NS_IsMainThread());mCompositingFailed=val;}voidimgFrame::AddSizeOfExcludingThis(MallocSizeOfaMallocSizeOf,size_t&aHeapSizeOut,size_t&aNonHeapSizeOut,size_t&aSharedHandlesOut)const{MonitorAutoLocklock(mMonitor);if(mPalettedImageData){aHeapSizeOut+=aMallocSizeOf(mPalettedImageData);}if(mLockedSurface){aHeapSizeOut+=aMallocSizeOf(mLockedSurface);}if(mOptSurface){aHeapSizeOut+=aMallocSizeOf(mOptSurface);}if(mRawSurface){aHeapSizeOut+=aMallocSizeOf(mRawSurface);mRawSurface->AddSizeOfExcludingThis(aMallocSizeOf,aHeapSizeOut,aNonHeapSizeOut);if(mRawSurface->GetType()==SurfaceType::DATA_SHARED){autosharedSurface=static_cast<SourceSurfaceSharedData*>(mRawSurface.get());if(sharedSurface->CanShare()){++aSharedHandlesOut;}}}}}// namespace image}// namespace mozilla